/*****************************************************************
JADE - Java Agent DEvelopment Framework is a framework to develop 
multi-agent systems in compliance with the FIPA specifications.
Copyright (C) 2000 CSELT S.p.A. 

GNU Lesser General Public License

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, 
version 2.1 of the License. 

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA  02111-1307, USA.
 *****************************************************************/

package market;

import jade.core.Agent;
import jade.core.AID;
import jade.core.behaviours.*;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;

import java.util.*;
import java.lang.Integer;

public class CustomerAgent extends Agent {
	// The title of the product to buy
	private String targetProductTitle;
	// Iteration time for checking products
	private int timeOfCheckingForProducts;
	// The list of known seller agents
	private AID[] sellerAgents;
	// GUI
	private CustomerAgentGUI myGui;
	// is client infinite 
	// 0 - no
	// 1 - yes
	private int isInfinite;

	// Put agent initializations here
	protected void setup() {
		// Printout a welcome message
		System.out.println("Hello! Customer-agent "+getAID().getName()+" is ready.");
		
		// Showing custom gui
		myGui = new CustomerAgentGUI(this);
		// Disabled for now
		//myGui.showGui();
		isInfinite = 0;
		
		// args[0] - time for checking of products - later CHANGED to random.
		// Get the title of the product to buy as a start-up argument
		Object[] args = getArguments();
			targetProductTitle = "Beer";
			System.out.println("Target product is "+targetProductTitle);
			timeOfCheckingForProducts = 10000;
			if(args.length>0)
			{
				try {
					timeOfCheckingForProducts = Integer.parseInt((String) args[0]);
				} catch (NumberFormatException e) {
					System.err.println("Argument must be an integer");
					System.exit(1);
				}	
			}
			if(args.length>1)
			{
				try {
					isInfinite = Integer.parseInt((String) args[1]);
				} catch (NumberFormatException e) {
					System.err.println("Argument must be an integer");
					System.exit(1);
				}	
			}
			System.out.println("Checking iteration is: "+timeOfCheckingForProducts);
			// Add a TickerBehaviour that schedules a request to seller agents every minute
			addBehaviour(new TickerBehaviour(this, timeOfCheckingForProducts) {
				protected void onTick() {
					System.out.println("["+getAID().getName()+"]Trying to buy "+targetProductTitle);
					// Update the list of seller agents
					DFAgentDescription template = new DFAgentDescription();
					ServiceDescription sd = new ServiceDescription();
					sd.setType("product-selling");
					template.addServices(sd);
					try {
						DFAgentDescription[] result = DFService.search(myAgent, template); 
						System.out.println("["+getAID().getName()+"]Found the following shop agents:");
						sellerAgents = new AID[result.length];
						for (int i = 0; i < result.length; ++i) {
							sellerAgents[i] = result[i].getName();
							System.out.println(sellerAgents[i].getName());
						}
					}
					catch (FIPAException fe) {
						fe.printStackTrace();
					}

					// Perform the request
					myAgent.addBehaviour(new RequestPerformer());
				}
			} );
	}

	// Put agent clean-up operations here
	protected void takeDown() {
		// Printout a dismissal message
		System.out.println("Customer-agent "+getAID().getName()+" terminating.");
	}

	/**
     This is invoked by the GUI when the user wants to but item NOW
	 */
	public void updateCatalogue(String title) {
		title = "Beer";// We sell only beer
		addBehaviour(new OneShotBehaviour() {
			public void action() {
				System.out.println("Customer-agent "+getAID().getName()+" is perforimng manula check for godly beer :)");
				//RequestPerformer.action();
			}
		} );
	}
	/**
	   Inner class RequestPerformer.
	   This is the behaviour used by Customer-buyer agents to request shop 
	   agents the target product.
	 */
	private class RequestPerformer extends Behaviour {
		private AID bestSeller; // The agent who provides the best offer 
		private int bestPrice;  // The best offered price
		private int repliesCnt = 0; // The counter of replies from shop agents
		private MessageTemplate mt; // The template to receive replies
		private int step = 0;

		public void action() {
			switch (step) {
			case 0:
				// Send the cfp to all sellers
				ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
				for (int i = 0; i < sellerAgents.length; ++i) {
					cfp.addReceiver(sellerAgents[i]);
				} 
				cfp.setContent(targetProductTitle);
				cfp.setConversationId("product-trade");
				cfp.setReplyWith("cfp"+System.currentTimeMillis()); // Unique value
				myAgent.send(cfp);
				// Prepare the template to get proposals
				mt = MessageTemplate.and(MessageTemplate.MatchConversationId("product-trade"),
						MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
				step = 1;
				break;
			case 1:
				// Receive all proposals/refusals from seller agents
				ACLMessage reply = myAgent.receive(mt);
				if (reply != null) {
					// Reply received
					if (reply.getPerformative() == ACLMessage.PROPOSE) {
						// This is an offer 
						int price = Integer.parseInt(reply.getContent());
						if (bestSeller == null || price < bestPrice) {
							// This is the best offer at present
							bestPrice = price;
							bestSeller = reply.getSender();
						}
					}
					repliesCnt++;
					if (repliesCnt >= sellerAgents.length) {
						// We received all replies
						step = 2; 
					}
				}
				else {
					block();
				}
				break;
			case 2:
				// Send the purchase order to the seller that provided the best offer
				ACLMessage order = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
				order.addReceiver(bestSeller);
				order.setContent(targetProductTitle);
				order.setConversationId("product-trade");
				order.setReplyWith("order"+System.currentTimeMillis());
				myAgent.send(order);
				// Prepare the template to get the purchase order reply
				mt = MessageTemplate.and(MessageTemplate.MatchConversationId("product-trade"),
						MessageTemplate.MatchInReplyTo(order.getReplyWith()));
				step = 3;
				break;
			case 3:      
				// Receive the purchase order reply
				reply = myAgent.receive(mt);
				if (reply != null) {
					// Purchase order reply received
					if (reply.getPerformative() == ACLMessage.INFORM) {
						// Purchase successful. We can terminate
						System.out.println(targetProductTitle+" successfully purchased from agent "+reply.getSender().getName());
						System.out.println("Price = "+bestPrice);
						if(isInfinite == 0)
						{
							myAgent.doDelete();
						}
					}
					else {
						System.out.println("Attempt failed: requested product already sold.");
					}

					step = 4;
				}
				else {
					block();
				}
				break;
			}        
		}

		public boolean done() {
			if (step == 2 && bestSeller == null) {
				System.out.println("Attempt failed: "+targetProductTitle+" not available for sale");
			}
			return ((step == 2 && bestSeller == null) || step == 4);
		}
	}  // End of inner class RequestPerformer
}
